/*
 * Copyright 2011-2011 Tiago da Fonseca Frazao  All rights reserved.
 */
package mz.co.research.server.mvc.model.dao.jpa;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import mz.co.framework.error.DaoException;

import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

/**
 * @author Tiago da Fonseca Frazao
 */
public abstract class AbstractJpaDao<K, E> extends JpaDaoSupport {

	protected Class<E> entityClass;

	@SuppressWarnings("unchecked")
	public AbstractJpaDao() {
		ParameterizedType genericSuperclass = (ParameterizedType) getClass()
				.getGenericSuperclass();
		this.entityClass = (Class<E>) genericSuperclass
				.getActualTypeArguments()[1];
	}

	public void persist(E entity) throws DaoException {

		try {
			getJpaTemplate().persist(entity);
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public void remove(E entity) throws DaoException {
		try {
			getJpaTemplate().remove(entity);
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public E merge(E entity) throws DaoException {
		try {
			return getJpaTemplate().merge(entity);
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public void refresh(E entity) throws DaoException {
		try {
			getJpaTemplate().refresh(entity);
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public E findById(K id) throws DaoException {
		try {
			return getJpaTemplate().find(entityClass, id);
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public E findByUnique(final CriteriaQuery<E> criteriaQuery)
			throws DaoException {
		try {
			return getJpaTemplate().execute(new JpaCallback<E>() {

				public E doInJpa(EntityManager em) throws PersistenceException {
					return em.createQuery(criteriaQuery).getSingleResult();
				}
			});
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public Collection<E> findByQuery(final CriteriaQuery<E> criteriaQuery)
			throws DaoException {
		try {
			return getJpaTemplate().execute(new JpaCallback<Collection<E>>() {

				public Collection<E> doInJpa(EntityManager em) throws PersistenceException {
					return em.createQuery(criteriaQuery).getResultList();
				}
			});
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
	}

	public E flush(E entity) throws DaoException {
		try {
			getJpaTemplate().flush();
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}
		return entity;
	}

	@SuppressWarnings("unchecked")
	public List<E> findAll() throws DaoException {
		Object res = null;
		try {
			res = getJpaTemplate().execute(new JpaCallback<Object>() {

				public Object doInJpa(EntityManager em)
						throws PersistenceException {
					Query q = em.createQuery("SELECT h FROM "
							+ entityClass.getName() + " h");
					return q.getResultList();
				}

			});
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.DAO_ERROR, e);
		}

		return (List<E>) res;
	}

	public Integer removeAll() throws DaoException {
		try {
			return (Integer) getJpaTemplate().execute(
					new JpaCallback<Object>() {

						public Object doInJpa(EntityManager em)
								throws PersistenceException {
							Query q = em.createQuery("DELETE FROM "
									+ entityClass.getName() + " h");
							return q.executeUpdate();
						}

					});
		} catch (PersistenceException e) {
			throw new DaoException(DaoException.GLOBAL, e);
		}
	}
}
